{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "IPython.notebook.set_autosave_interval(600000)"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Autosaving every 600 seconds\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "%autosave 600"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "from scikits.odes.odeint import odeint"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on function odeint in module scikits.odes.odeint:\n",
      "\n",
      "odeint(rhsfun, tout, y0, method='bdf', **options)\n",
      "    Integrate a system of ordinary differential equations.\n",
      "    *odeint* is a wrapper around the ode class, as a confenience function to\n",
      "    quickly integrate a system of ode.\n",
      "    Solves the initial value problem for stiff or non-stiff systems\n",
      "    of first order ode's:\n",
      "    \n",
      "        rhs = dy/dt = fun(t, y)\n",
      "    \n",
      "    where y can be a vector, then rhsfun must be a function computing rhs with\n",
      "    signature:\n",
      "    \n",
      "        rhsfun(t, y, rhs)\n",
      "    \n",
      "    storing the computated dy/dt in the rhs array passed to the function\n",
      "    \n",
      "    Parameters\n",
      "    ----------\n",
      "    rhsfun : callable(t, y, out)\n",
      "        Computes the derivative at dy/dt in terms of t and y, and stores in out\n",
      "    y0 : array\n",
      "        Initial condition on y (can be a vector).\n",
      "    t : array\n",
      "        A sequence of time points for which to solve for y.  The initial\n",
      "        value point should be the first element of this sequence.\n",
      "    method : string, solution  method to use.\n",
      "        Available are all the ode class solvers as well as some convenience\n",
      "        shorthands:\n",
      "    \n",
      "        =======  ==============================================================\n",
      "        Method   Meaning\n",
      "        =======  ==============================================================\n",
      "        bdf      This uses the 'cvode' solver in default from, which is a\n",
      "                 variable step, variable coefficient Backward Differentiation\n",
      "                 Formula solver, good for stiff ODE. Newton iterations are\n",
      "                 used to solve the nonlinear system.\n",
      "        admo     This uses the 'cvode' solver with option lmm_type='ADAMS',\n",
      "                 which is a variable step Adams-Moulton method (linear\n",
      "                 multistep method), good for non-stiff ODE. Functional\n",
      "                 iterations are used to solve the nonlinear system.\n",
      "        rk5      This uses the 'dopri5' solver, which is a variable step\n",
      "                 Runge-Kutta method of order (4)5 (use for non-stiff ODE)\n",
      "        rk8      This uses the 'dop853' solver, which is a variable step\n",
      "                 Runge-Kutta method of order 8(5,3)\n",
      "        =======  ==============================================================\n",
      "    \n",
      "        For educational purposes, you can also access following methods:\n",
      "    \n",
      "        =======  ==============================================================\n",
      "        Method   Meaning\n",
      "        =======  ==============================================================\n",
      "        beuler   This is the Implicit Euler (backward Euler) method (order 1),\n",
      "                 which is obtained via the 'bdf' method, setting the order\n",
      "                 option to 1, setting large tolerances,  and fixing the\n",
      "                 stepsize.\n",
      "                 Use option 'step' to change stepsize, default: step=0.05.\n",
      "                 Use option 'rtol' and 'atol' to use more strict tolerances\n",
      "                 Note: this is not completely the backward Euler method, as\n",
      "                 the cvode solver has added control options!\n",
      "        trapz    This is the Trapezoidal Rule method (order 2), which is\n",
      "                 obtained via the 'admo' method, setting option order to 2,\n",
      "                 setting large tolerances and fixing the stepsize.\n",
      "                 Use option 'step' to change stepsize, default: step=0.05.\n",
      "                 Use option 'rtol' and 'atol' to use more strict tolerances\n",
      "                 Note: The cvode solver might change the order to 1 internally\n",
      "                 in which case this becomes beuler method. Set atol, rtol\n",
      "                 options as strict as possible.\n",
      "        =======  ==============================================================\n",
      "    \n",
      "        You can also access the solvers of ode via their names:\n",
      "    \n",
      "        =======  ==============================================================\n",
      "        Method   Meaning\n",
      "        =======  ==============================================================\n",
      "        cvode    This uses the 'cvode' solver\n",
      "        dopri5   This uses the 'dopri5' solver\n",
      "        dop853   This uses the 'dop853' solver\n",
      "        =======  ==============================================================\n",
      "    \n",
      "    options : extra solver options, optional\n",
      "        Every solver has it's own extra options, see the ode class and the\n",
      "        details of the solvers available there to know the options possible per\n",
      "        solver\n",
      "    \n",
      "    Returns\n",
      "    -------\n",
      "    solution : named tuple\n",
      "        A single named tuple is returned containing the result of the\n",
      "        integration.\n",
      "    \n",
      "        ========  ==========================================\n",
      "        Field     Meaning\n",
      "        ========  ==========================================\n",
      "        flag      An integer flag\n",
      "        values    Named tuple with fields t and y\n",
      "        errors    Named tuple with fields t and y\n",
      "        roots     Named tuple with fields t and y\n",
      "        tstop     Named tuple with fields t and y\n",
      "        message   String with message in case of an error\n",
      "        ========  ==========================================\n",
      "    \n",
      "    See Also\n",
      "    --------\n",
      "    ode : a more object-oriented integrator in scikits.odes\n",
      "    dae : a solver for differential-algebraic equations in scikits.odes\n",
      "    quad : for finding the area under a curve, part of scipy.\n",
      "    \n",
      "    Examples\n",
      "    --------\n",
      "    The second order differential equation for the angle `theta` of a\n",
      "    pendulum acted on by gravity with friction can be written:\n",
      "    \n",
      "    .. math:: \\theta''(t) + b \\theta'(t) + c \\sin(\\theta(t)) = 0\n",
      "    \n",
      "    where `b` and `c` are positive constants, and a prime (') denotes a\n",
      "    derivative.  To solve this equation with `odeint`, we must first convert\n",
      "    it to a system of first order equations.  By defining the angular\n",
      "    velocity ``omega(t) = theta'(t)``, we obtain the system:\n",
      "    \n",
      "    .. math:: \\theta'(t) = \\omega(t)\n",
      "     \\omega'(t) = -b \\omega(t) - c \\sin(\\theta(t))\n",
      "    \n",
      "    We assume the constants are `b` = 0.25 and `c` = 5.0:\n",
      "    \n",
      "    >>> b = 0.25\n",
      "    >>> c = 5.0\n",
      "    \n",
      "    Let `y` be the vector [`theta`, `omega`].  We implement this system\n",
      "    in python as:\n",
      "    \n",
      "    >>> def pend(t, y, out):\n",
      "     ...     theta, omega = y\n",
      "     ...     out[:] = [omega, -b*omega - c*np.sin(theta)]\n",
      "     ...\n",
      "    \n",
      "    In case you want b and c easily changable, make pend a class method, and\n",
      "    consider attributes b and c accessible via `self.b` and `self.c`.\n",
      "    For initial conditions, we assume the pendulum is nearly vertical\n",
      "    with `theta(0)` = `pi` - 0.1, and it initially at rest, so\n",
      "    `omega(0)` = 0.  Then the vector of initial conditions is\n",
      "    \n",
      "    >>> y0 = [np.pi - 0.1, 0.0]\n",
      "    \n",
      "    We generate a solution 101 evenly spaced samples in the interval\n",
      "    0 <= `t` <= 10.  So our array of times is\n",
      "    \n",
      "    >>> t = np.linspace(0, 10, 101)\n",
      "    \n",
      "    Call `odeint` to generate the solution.\n",
      "    \n",
      "    >>> from scikits.odes.odeint import odeint\n",
      "    >>> sol = odeint(pend, t, y0)\n",
      "    \n",
      "    The solution is a named tuple `sol`. sol.values.y is an array with shape (101, 2).\n",
      "    The first column is `theta(t)`, and the second is `omega(t)`.\n",
      "    The following code plots both components.\n",
      "    \n",
      "    >>> import matplotlib.pyplot as plt\n",
      "    >>> plt.plot(sol.values.t, sol.values.y[:, 0], 'b', label='theta(t)')\n",
      "    >>> plt.plot(sol.values.t, sol.values.y[:, 1], 'g', label='omega(t)')\n",
      "    >>> plt.legend(loc='best')\n",
      "    >>> plt.xlabel('t')\n",
      "    >>> plt.grid()\n",
      "    >>> plt.show()\n",
      "\n"
     ]
    }
   ],
   "source": [
    "help(odeint)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "#from scipy.integrate import solve_ivp"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For the following the Cython module must be installed: pip3 install Cython"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/usr/local/lib/python3.5/dist-packages/Cython/Distutils/old_build_ext.py:30: UserWarning: Cython.Distutils.old_build_ext does not properly handle dependencies and is deprecated.\n",
      "  \"Cython.Distutils.old_build_ext does not properly handle dependencies \"\n"
     ]
    }
   ],
   "source": [
    "%load_ext Cython"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def fun(t, y, out):\n",
    "    mu = 50\n",
    "    out[0] = y[1]\n",
    "    out[1] =  mu * (1 - y[0] ** 2) * y[1] - y[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def jac(t, y, out):\n",
    "    mu = 50\n",
    "    out[:,:] = [\n",
    "        [0, 1],\n",
    "        [-2 * mu * y[0] * y[1] - 1, mu * (1 - y[0] ** 2)]]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "%%cython\n",
    "def fun_cython(double t, double[:] y, double [:] f):\n",
    "    cdef double mu = 50\n",
    "    f[0] = y[1]\n",
    "    f[1] = mu * (1 - y[0] * y[0]) * y[1] - y[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "%%cython -I /home/benny/git/odes/scikits/odes/sundials/ -I /usr/local/lib/python3.5/dist-packages/scikits.odes-2.3.0.dev0-py3.5-linux-x86_64.egg/scikits/odes/sundials/\n",
    "### UPDATE ABOVE -I flag to installed odes/sundials sources !!\n",
    "import numpy as np\n",
    "cimport numpy as np\n",
    "from scikits.odes.sundials.cvode cimport CV_RhsFunction\n",
    "    \n",
    "#scikits.odes allows cython functions only if derived from correct class\n",
    "cdef class RhsFunction(CV_RhsFunction):\n",
    "    cpdef int evaluate(self, double t,\n",
    "                       np.ndarray[double, ndim=1] y,\n",
    "                       np.ndarray[double, ndim=1] f,\n",
    "                       object userdata = None) except? -1:\n",
    "        cdef double mu = 50\n",
    "        f[0] = y[1]\n",
    "        f[1] = mu * (1 - y[0] * y[0]) * y[1] - y[0]\n",
    "        return 0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "y0 = [2, 0]\n",
    "outtimes = np.linspace(0, 1000, 101)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "sol1 = odeint(fun, outtimes, y0, method='cvode')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      " "
     ]
    }
   ],
   "source": [
    "stats = %prun -r -q odeint(fun, outtimes, y0, method='cvode')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "         15637 function calls in 0.062 seconds\n",
      "\n",
      "   Ordered by: internal time\n",
      "   List reduced from 47 to 20 due to restriction <20>\n",
      "\n",
      "   ncalls  tottime  percall  cumtime  percall filename:lineno(function)\n",
      "        1    0.045    0.045    0.062    0.062 {method 'solve' of 'scikits.odes.sundials.cvode.CVODE' objects}\n",
      "    15534    0.017    0.000    0.017    0.000 <ipython-input-6-d6e24033dc3f>:1(fun)\n",
      "        1    0.000    0.000    0.062    0.062 {built-in method builtins.exec}\n",
      "        1    0.000    0.000    0.000    0.000 inspect.py:2065(_signature_from_function)\n",
      "        2    0.000    0.000    0.000    0.000 copy.py:67(copy)\n",
      "        1    0.000    0.000    0.000    0.000 inspect.py:1052(getfullargspec)\n",
      "        1    0.000    0.000    0.062    0.062 <string>:1(<module>)\n",
      "        1    0.000    0.000    0.000    0.000 ode.py:220(__init__)\n",
      "        1    0.000    0.000    0.000    0.000 {built-in method _warnings.warn}\n",
      "        1    0.000    0.000    0.000    0.000 inspect.py:1027(getargspec)\n",
      "        2    0.000    0.000    0.000    0.000 {built-in method numpy.core.multiarray.array}\n",
      "        1    0.000    0.000    0.000    0.000 inspect.py:2146(_signature_from_callable)\n",
      "        1    0.000    0.000    0.062    0.062 odeint.py:13(odeint)\n",
      "        3    0.000    0.000    0.000    0.000 inspect.py:2399(__init__)\n",
      "        1    0.000    0.000    0.000    0.000 inspect.py:2664(__init__)\n",
      "        7    0.000    0.000    0.000    0.000 {built-in method __new__ of type object at 0xa3ddc0}\n",
      "        1    0.000    0.000    0.000    0.000 <string>:12(__new__)\n",
      "        1    0.000    0.000    0.000    0.000 enum.py:215(__call__)\n",
      "        1    0.000    0.000    0.000    0.000 ode.py:445(find_ode_integrator)\n",
      "        2    0.000    0.000    0.000    0.000 numeric.py:2064(isscalar)\n",
      "\n",
      "\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<pstats.Stats at 0x7f21a89809e8>"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "stats.print_stats(20)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "sol2 = odeint(RhsFunction(), outtimes, y0, method='cvode')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      " "
     ]
    }
   ],
   "source": [
    "stats = %prun -r -q odeint(RhsFunction(), outtimes, y0, method='cvode')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "         49 function calls in 0.042 seconds\n",
      "\n",
      "   Ordered by: internal time\n",
      "   List reduced from 27 to 20 due to restriction <20>\n",
      "\n",
      "   ncalls  tottime  percall  cumtime  percall filename:lineno(function)\n",
      "        1    0.041    0.041    0.042    0.042 {method 'solve' of 'scikits.odes.sundials.cvode.CVODE' objects}\n",
      "        2    0.000    0.000    0.000    0.000 copy.py:67(copy)\n",
      "        1    0.000    0.000    0.042    0.042 {built-in method builtins.exec}\n",
      "        1    0.000    0.000    0.042    0.042 <string>:1(<module>)\n",
      "        1    0.000    0.000    0.000    0.000 ode.py:220(__init__)\n",
      "        4    0.000    0.000    0.000    0.000 <string>:12(__new__)\n",
      "        2    0.000    0.000    0.000    0.000 {built-in method numpy.core.multiarray.array}\n",
      "        1    0.000    0.000    0.042    0.042 odeint.py:13(odeint)\n",
      "        1    0.000    0.000    0.000    0.000 {method 'match' of '_sre.SRE_Pattern' objects}\n",
      "        2    0.000    0.000    0.000    0.000 numeric.py:414(asarray)\n",
      "        1    0.000    0.000    0.000    0.000 enum.py:215(__call__)\n",
      "        5    0.000    0.000    0.000    0.000 fromnumeric.py:2362(alen)\n",
      "        1    0.000    0.000    0.000    0.000 ode.py:445(find_ode_integrator)\n",
      "        2    0.000    0.000    0.000    0.000 numeric.py:2064(isscalar)\n",
      "        1    0.000    0.000    0.000    0.000 enum.py:459(__new__)\n",
      "        1    0.000    0.000    0.000    0.000 re.py:160(match)\n",
      "        1    0.000    0.000    0.042    0.042 ode.py:282(solve)\n",
      "        1    0.000    0.000    0.000    0.000 re.py:278(_compile)\n",
      "        2    0.000    0.000    0.000    0.000 fromnumeric.py:1574(shape)\n",
      "        5    0.000    0.000    0.000    0.000 {built-in method __new__ of type object at 0xa3ddc0}\n",
      "\n",
      "\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<pstats.Stats at 0x7f21a8965860>"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "stats.print_stats(20)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "   0               2               2\n",
      "  10         1.85832         1.85832\n",
      "  20         1.69338         1.69338\n",
      "  30          1.4849          1.4849\n",
      "  40         1.09322         1.09322\n",
      "  50        -1.87714        -1.87714\n",
      "  60        -1.71581        -1.71581\n",
      "  70        -1.51498        -1.51498\n",
      "  80        -1.18599        -1.18599\n",
      "  90         1.89563         1.89563\n",
      " 100         1.73766         1.73766\n",
      " 110         1.54362         1.54362\n",
      " 120         1.24882         1.24882\n",
      " 130         -1.9138         -1.9138\n",
      " 140        -1.75896        -1.75896\n",
      " 150        -1.57099        -1.57099\n",
      " 160         -1.2998         -1.2998\n",
      " 170         1.93167         1.93167\n",
      " 180         1.77978         1.77978\n",
      " 190          1.5973          1.5973\n",
      " 200         1.34404         1.34404\n",
      " 210        -1.94927        -1.94927\n",
      " 220        -1.80014        -1.80014\n",
      " 230        -1.62265        -1.62265\n",
      " 240        -1.38373        -1.38373\n",
      " 250          1.9666          1.9666\n",
      " 260         1.82008         1.82008\n",
      " 270         1.64715         1.64715\n",
      " 280          1.4201          1.4201\n",
      " 290        -1.98367        -1.98367\n",
      " 300        -1.83962        -1.83962\n",
      " 310        -1.67088        -1.67088\n",
      " 320        -1.45391        -1.45391\n",
      " 330         2.00049         2.00049\n",
      " 340         1.85878         1.85878\n",
      " 350         1.69392         1.69392\n",
      " 360         1.48565         1.48565\n",
      " 370         1.09628         1.09628\n",
      " 380        -1.87759        -1.87759\n",
      " 390        -1.71633        -1.71633\n",
      " 400        -1.51568        -1.51568\n",
      " 410        -1.18771        -1.18771\n",
      " 420         1.89607         1.89607\n",
      " 430         1.73817         1.73817\n",
      " 440         1.54428         1.54428\n",
      " 450         1.25013         1.25013\n",
      " 460        -1.91423        -1.91423\n",
      " 470        -1.75947        -1.75947\n",
      " 480        -1.57164        -1.57164\n",
      " 490        -1.30094        -1.30094\n",
      " 500         1.93211         1.93211\n",
      " 510         1.78028         1.78028\n",
      " 520         1.59792         1.59792\n",
      " 530         1.34505         1.34505\n",
      " 540        -1.94969        -1.94969\n",
      " 550        -1.80063        -1.80063\n",
      " 560        -1.62325        -1.62325\n",
      " 570        -1.38465        -1.38465\n",
      " 580         1.96701         1.96701\n",
      " 590         1.82056         1.82056\n",
      " 600         1.64773         1.64773\n",
      " 610         1.42095         1.42095\n",
      " 620        -1.98408        -1.98408\n",
      " 630        -1.84008        -1.84008\n",
      " 640        -1.67145        -1.67145\n",
      " 650         -1.4547         -1.4547\n",
      " 660          2.0009          2.0009\n",
      " 670         1.85924         1.85924\n",
      " 680         1.69447         1.69447\n",
      " 690         1.48639         1.48639\n",
      " 700         1.09929         1.09929\n",
      " 710        -1.87804        -1.87804\n",
      " 720        -1.71687        -1.71687\n",
      " 730        -1.51639        -1.51639\n",
      " 740        -1.18943        -1.18943\n",
      " 750         1.89652         1.89652\n",
      " 760          1.7387          1.7387\n",
      " 770         1.54496         1.54496\n",
      " 780         1.25148         1.25148\n",
      " 790        -1.91467        -1.91467\n",
      " 800        -1.75998        -1.75998\n",
      " 810         -1.5723         -1.5723\n",
      " 820        -1.30209        -1.30209\n",
      " 830         1.93254         1.93254\n",
      " 840         1.78078         1.78078\n",
      " 850         1.59855         1.59855\n",
      " 860         1.34606         1.34606\n",
      " 870        -1.95012        -1.95012\n",
      " 880        -1.80113        -1.80113\n",
      " 890        -1.62387        -1.62387\n",
      " 900        -1.38558        -1.38558\n",
      " 910         1.96744         1.96744\n",
      " 920         1.82105         1.82105\n",
      " 930         1.64833         1.64833\n",
      " 940         1.42182         1.42182\n",
      " 950         -1.9845         -1.9845\n",
      " 960        -1.84056        -1.84056\n",
      " 970        -1.67203        -1.67203\n",
      " 980        -1.45551        -1.45551\n",
      " 990         2.00131         2.00131\n",
      "1000         1.85971         1.85971\n"
     ]
    }
   ],
   "source": [
    "#verify the two solutions are equal\n",
    "for t, u1, u2 in zip(sol1.values.t, sol1.values.y,  sol2.values.y):\n",
    "    print('{0:>4.0f} {1:15.6g} {2:15.6g}'.format(t, u1[0], u2[0]) )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
